using System;
using System.Collections.Generic;

namespace WorkFlow.Utils
{
    public enum EachResult
    {
        Break,
        Continue
    }
    public class CustomObjectPool<T> : IDisposable  where T : class
    {
        public delegate T CreateObject();
        public delegate void Realease(T obj);
        public delegate void DestroyObject(T obj);

        private Stack<T> m_Stack;
        private HashSet<T> m_UsedList;
        int maxSize = 0;

        CreateObject m_CreateObject;
        Realease m_ReleaseObject;
        DestroyObject m_DestroyObject;
        public CustomObjectPool(CreateObject createObject, Realease releaseObject, DestroyObject destroyObject,int defaultCapacity = 10, int maxSize = 10000)
        {
            m_Stack = new Stack<T>(defaultCapacity);
            m_UsedList = new HashSet<T>(defaultCapacity);
            this.maxSize = maxSize;
            m_CreateObject = createObject;
            m_ReleaseObject = releaseObject;
            m_DestroyObject = destroyObject;
        }

        public T Get()
        {
            T element;
            if (m_Stack.Count == 0)
            {
                element = m_CreateObject();
            }
            else
            {
                element = m_Stack.Pop();
            }
            m_UsedList.Add(element);
            return element;
        }

        public void Release(T element)
        {
            foreach (var item in m_UsedList)
            {
                if (item.Equals(element))
                {
                    m_UsedList.Remove(item);
                    if (m_Stack.Count + m_UsedList.Count > maxSize)
                    {
                        m_DestroyObject?.Invoke(item);
                        break;
                    }
                    m_Stack.Push(element);
                    m_ReleaseObject?.Invoke(element);
                    break;
                }
            }
        }
        public void ReleaseAll()
        {
            int count = m_Stack.Count + m_UsedList.Count;
            foreach (var item in m_UsedList)
            {
                if (count > maxSize)
                {
                    m_DestroyObject?.Invoke(item);
                    count--;
                    continue;
                }
                m_ReleaseObject?.Invoke(item);
                m_Stack.Push(item);
            }
            m_UsedList.Clear();
        }

        public void Clear()
        {
            foreach (var item in m_Stack)
            {
                m_DestroyObject?.Invoke(item);
            }
            m_Stack.Clear();
            foreach (var item in m_UsedList)
            {
                m_DestroyObject?.Invoke(item);
            }
            m_UsedList.Clear();
        }

        public int LiveCount => m_UsedList.Count;

        public int WaitCount => m_Stack.Count;

        public void ForeachLive(System.Func<T, EachResult> eachItem)
        {
            if(eachItem == null)
            {
                return;
            }
            foreach (var item in m_UsedList)
            {
                if (eachItem(item) == EachResult.Break)
                {
                    break;
                }
            }
        }
        void IDisposable.Dispose()
        {
            Clear();
        }
    }
}
